package cn.yunyichina.reservation.controller.business;

import cn.yunyichina.provider.framework.dubbo.service.ReservationService;
import cn.yunyichina.provider.iface.entity.common.Response;
import cn.yunyichina.reservation.common.jackson.Jackson;
import cn.yunyichina.reservation.constant.PayConst;
import cn.yunyichina.reservation.constant.Config;
import cn.yunyichina.reservation.constant.Encoding;
import cn.yunyichina.reservation.constant.Interface;
import cn.yunyichina.reservation.constant.PayType;
import cn.yunyichina.reservation.util.MoneyUtil;
import cn.yunyichina.reservation.util.ResponseUtil;
import cn.yunyichina.reservation.vo.OrderVo;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

/**
 * Created by Lullaby on 2016/9/1.
 */
@RestController
@RequestMapping("pay/notify")
public class PayNotifyController {

    private static final Logger logger = LoggerFactory.getLogger(PayNotifyController.class);

    private static final String RESERVATION_ORDER_DETAIL = "yyt.reservation.order.detail";
    private static final String RESERVATION_ORDER_CALLBACK_UPDATE = "yyt.reservation.order.callback.update";

    @Resource
    private Properties yamlProperties;

    @RequestMapping("wechat")
    public void wechatPayNotify(HttpServletRequest request, HttpServletResponse response) {
        logger.info(">>>>>>>微信支付回调<<<<<<<");
        Map<String, String> params = this.getNotifyParameters(request);
        logger.info("[微信支付回调 - 入参 -> {}]", Jackson.getBaseJsonData(params));
        boolean isSuccesss = false;
        if (!CollectionUtils.isEmpty(params)) {
            String returnCode = params.get(PayConst.WECHAT_RETURN_CODE);
            String resultCode = params.get(PayConst.WECHAT_RESULT_CODE);
            String returnMsg = params.get(PayConst.WECHAT_RETURN_MESSAGE);
            if (StringUtils.isNotBlank(returnCode) && PayConst.WECHAT_SUCCESS_CODE.equals(returnCode)) {
                logger.info("[微信支付回调 - [RETURN_CODE] -> SUCCESS]");
                if (StringUtils.isNotBlank(resultCode) && PayConst.WECHAT_SUCCESS_CODE.equals(resultCode)) {
                    if (!checkSign(params)) {
                        Map<String, String> orderNoParam = new HashMap<>();
                        String outTradeNo = params.get("out_trade_no");
                        orderNoParam.put("order_no", outTradeNo);
                        Response serviceResponse = ReservationService.callHumpFromInternal(
                                RESERVATION_ORDER_DETAIL, Jackson.getBaseJsonData(orderNoParam)
                        );
                        if (serviceResponse == null || !Interface.SUCCESS.equals(serviceResponse.getResultCode())) {
                            logger.warn("Service not available.");
                        } else if (Interface.SUCCESS.equals(serviceResponse.getResultCode())) {
                            OrderVo orderVo = Jackson.getBeanJsonData(serviceResponse.getResult(), OrderVo.class);
                            if (orderVo != null) {
                                Integer isCallBack = orderVo.getIsPayCallback();
                                if (isCallBack != null && isCallBack != 1) {
                                    Map<String, String> updateParams = new HashMap<>();
                                    updateParams.put("id", orderVo.getId());
                                    updateParams.put("pay_order_no", params.get("transaction_id"));
                                    updateParams.put("pay_type", String.valueOf(PayType.WECHAT_PAY));
                                    updateParams.put("pay_cost", String.valueOf(MoneyUtil.divide(Float.valueOf(params.get("total_fee")), 100)));
                                    updateParams.put("pay_end_time", params.get("time_end"));
                                    updateParams.put("open_id", params.get("openid"));
                                    Response updateResponse = ReservationService.callHumpFromInternal(
                                            RESERVATION_ORDER_CALLBACK_UPDATE, Jackson.getBaseJsonData(updateParams)
                                    );
                                    if (updateResponse != null && Interface.SUCCESS.equals(updateResponse.getResultCode())) {
                                        logger.info("Callback success.");
                                        isSuccesss = true;
                                    } else {
                                        logger.warn("Callback failure.");
                                    }
                                } else {
                                    logger.warn("Duplicate callback.");
                                    isSuccesss = true;
                                }
                            } else {
                                logger.warn("Order [{}] not found.", outTradeNo);
                            }
                        }
                    } else {
                        logger.warn("[微信支付回调 - 验证签名失败]");
                    }
                } else if (StringUtils.isNotBlank(resultCode) && PayConst.WECHAT_FAILURE_CODE.equals(resultCode)) {
                    logger.info("[微信支付回调 - [RESULT_CODE] -> FAIL - [RETURN_MSG] -> {}]", returnMsg);
                } else {
                    logger.info("[微信支付回调 - [RESULT_CODE] -> 状态异常] -> {}]", returnMsg);
                }
            } else if (StringUtils.isNotBlank(returnCode) && PayConst.WECHAT_FAILURE_CODE.equals(returnCode)) {
                logger.warn("[微信支付回调 - [RETURN_CODE] -> FAIL - [RETURN_MSG] -> {}]", returnMsg);
            } else {
                logger.warn("[微信支付回调 - [RETURN_CODE] - 状态异常]");
            }
        } else {
            logger.warn("[微信支付回调 - 入参为空]");
        }
        if (isSuccesss) {
            ResponseUtil.response(getSuccessMessage(), response);
        } else {
            ResponseUtil.response(getFailureMessage(), response);
        }
    }

    private boolean checkSign(Map<String, String> params) {
        String sign = params.get("sign");
        params.remove("sign");
        String[] keys = new String[params.size()];
        params.keySet().toArray(keys);
        Arrays.sort(keys);
        StringBuffer target = new StringBuffer();
        for (String key : keys) {
            String value = params.get(key);
            if (StringUtils.isNotBlank(value)) {
                target.append(key).append("=").append(value).append("&");
            }
        }
        target.append("key=").append(yamlProperties.getProperty(Config.WECHAT_PAY_PAYSECRET));
        return sign.equals(DigestUtils.md5Hex(target.toString()));
    }

    private String getSuccessMessage() {
        StringBuffer success = new StringBuffer();
        success.append("<xml>")
                .append("<return_code><![CDATA[SUCCESS]]></return_code>")
                .append("<return_msg><![CDATA[OK]]></return_msg>")
                .append("</xml>");
        return success.toString();
    }

    private String getFailureMessage() {
        StringBuffer failure = new StringBuffer();
        failure.append("<xml>")
                .append("<return_code><![CDATA[FAIL]]></return_code>")
                .append("<return_msg><![CDATA[NOTOK]]></return_msg>")
                .append("</xml>");
        return failure.toString();
    }

    private Map<String, String> getNotifyParameters(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), Encoding.UTF8))) {
            StringBuilder buffer = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            Document doc = DocumentHelper.parseText(buffer.toString());
            Element root = doc.getRootElement();
            for (Iterator<Element> iterator = root.elementIterator(); iterator.hasNext(); ) {
                Element e = iterator.next();
                params.put(e.getName(), e.getText());
            }
        } catch (DocumentException e) {
            logger.error("[PayNotifyController.getNotifyParameters -> DocumentException]", e);
        } catch (IOException e) {
            logger.error("[PayNotifyController.getNotifyParameters -> IOException]", e);
        }
        return params;
    }

}
